//! @Author: DengLibin
//! @Date: Create in 2025-03-19 14:30:16
//! @Description:
//! 驱动webdriver 访问网站
// 1.下载chrome
// 2.下载对应版本的chrome driver
// 3.双击启动chrome driver 查看端口

use fantoccini::{Client, ClientBuilder, Locator};
use serde_json::Value;
use std::time::Duration;
use tokio::time::sleep;

use crate::sys::global::{to_global_result, GlobalResult};

pub struct WebDriver {
    client: Option<Client>,
}

impl Drop for WebDriver {
    fn drop(&mut self) {
        // 启动一个异步任务来调用 close()
        let client = self.client.take();
        if let Some(client) = client {
            let _ = tokio::spawn(async move {
                let _ = client.close().await;
            });
        }
    }
}

impl WebDriver {
    /// @Author: DengLibin
    /// @Date: Create in 2025-03-19 15:15:17
    /// @Description: webdriver 地址 "http://localhost:9515"
    pub async fn new(driver_url: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let client = ClientBuilder::native().connect(driver_url).await?; //chrome driver端口
        Ok(WebDriver {
            client: Some(client),
        })
    }

    /// @Author: DengLibin
    /// @Date: Create in 2025-03-19 14:59:32
    /// @Description: 获取文本内容
    pub async fn get_text(&self, url: &str) -> GlobalResult<String> {
       to_global_result(self.do_get_text(url).await)
    }
    /// @Author: DengLibin
    /// @Date: Create in 2025-03-19 14:59:32
    /// @Description: 获取文本内容
    async fn do_get_text(&self, url: &str) -> Result<String, Box<dyn std::error::Error>> {
        if let Some(client) = &self.client {
            client.goto(url).await?;
            wait_for_page_load(client).await?;
            return get_body_text(client).await;
        }
        Ok("".into())
    }
}

async fn get_body_text(client: &Client) -> Result<String, Box<dyn std::error::Error>> {
    let body_ele = client
        .wait()
        .for_element(Locator::XPath(r#"//body"#))
        .await?;
    let text = body_ele.text().await?;

    Ok(text)
}

async fn wait_for_page_load(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
    loop {
        let ready_state = client
            .execute("return document.readyState;", vec![])
            .await?;
        log::info!("wait to comepleted:{}", ready_state);

        if ready_state == Value::String("complete".into()) {
            break;
        }

        sleep(Duration::from_millis(500)).await; // 等待 500ms 再次检查
    }
    Ok(())
}

/// 访问京东网站
pub async fn demo() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to webdriver instance that is listening on port 4444
    let client = ClientBuilder::native()
        .connect("http://localhost:9515") //chrome driver端口
        .await?;

    //进入网站根目录
    client.goto("https://jd.com/").await?;

    //暂停 可以手动做登录操作
    sleep(Duration::from_millis(60 * 1000)).await;
    let search_input = client
        .wait()
        .for_element(Locator::XPath(r#"//*[@id="key"]"#))
        .await?;

    //搜索输入框输入
    search_input.send_keys("电脑").await?;

    let search_btn = client
        .wait()
        .for_element(Locator::XPath(r#"//*[@id="search"]/div/div[2]/button"#))
        .await?;
    //点击搜索
    search_btn.click().await?;
    //进入搜索结果页
    go_search_result(&client).await?;

    //返回
    client.back().await?;

    sleep(Duration::from_millis(30 * 1000)).await;
    // Then close the browser window.
    //关闭
    client.close().await?;
    Ok(())
}

//去到搜索结果页
async fn go_search_result(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
    //
    let goods_ul = client
        .wait()
        .for_element(Locator::XPath(r#"//*[@id="J_goodsList"]/ul"#))
        .await?;
    //所有商品
    let item_divs = goods_ul.find_all(Locator::Css("li .gl-i-wrap")).await?;
    //遍历
    for item_div in item_divs.iter() {
        //获取商品名称
        let name_em = item_div.find(Locator::Css(".p-name")).await?;
        //获取价格
        let price_i = item_div.find(Locator::Css(".p-price")).await?;
        let name = name_em.text().await?;
        let price = price_i.text().await?;
        println!("名称:{}，价格:{}", name, price);
    }
    Ok(())
}
